/* Ninja.cpp
 *
 *
 * Last Modified: 31.01.2014
 * Author: Guilherme S.G.
 *
 * TODO:
 */

#include "Ninja.h"
#include "Game.h"
#include "Tools.h"


// Auxiliar Function
std::string removeTrailing(const char *buffer_)
{
    int i = 0;
    std::string str = " ";
    
    while(buffer_[i] == ' ' || buffer_[i] == '\t')
        i++;
    
    str.assign(buffer_, i, strlen(buffer_));
    
    return str;
}


Ninja::Ninja(std::string name_, std::string element_, std::string family_, std::string description_) : coord(0,0)
{
    name = name_;
    element = element_;
    family = family_;
    description = description_;
    state = STAND;
    currentJutsu = nullptr;
    currentAnimation = nullptr;
    lookingDirection = 0;
}

Ninja::~Ninja()
{
    
}

void Ninja::addAnimation(Sprite *newAnimation_)
{
    animation.push_back(std::pair<std::string,Sprite*>(newAnimation_->getName(), newAnimation_));
}

void Ninja::addJutsu(Jutsu *newJutsu_)
{
    jutsus.push_back(newJutsu_);
}

void Ninja::addSealToStack(HandSeal *seal_)
{
    stack.push_back(seal_);
}

void Ninja::setPosition(double positionX_, double positionY_)
{
    coord = Coordinate(positionX_, positionY_);
    std::vector<std::pair<std::string,Sprite*> >::iterator iter;
    
    for(iter = animation.begin(); iter != animation.end(); iter++)
    {
        iter->second->setPosition(positionX_, positionY_);
    }
    
}

void Ninja::setAnimationParameters(int animationID_, int frameDelay_, int animationDirection_, int start_, int end_, int cycles_)
{
    std::vector<std::pair<std::string,Sprite*> >::iterator iter = animation.begin()+animationID_;
    
    iter->second->setParameters(frameDelay_, animationDirection_, start_, end_, cycles_);
}

HandSeal* Ninja::getStackTip()
{
    if(stack.empty())
        return nullptr;
    
    return stack[stack.size()-1];
}

Sprite* Ninja::getAnimation(std::string tag_)
{
    std::vector<std::pair<std::string, Sprite*> >::iterator iter;
    
    for(iter = animation.begin(); iter != animation.end(); iter++)
    {
        if(iter->first.compare(tag_.c_str()) == 0)
            return iter->second;
    }
    
    return nullptr;
}

Sprite* Ninja::getPortrait()
{
    std::vector<std::pair<std::string, Sprite*> >::iterator iter;
    
    for(iter = animation.begin(); iter != animation.end(); iter++)
    {
        if(iter->first.compare("portrait") == 0)
            return iter->second;
    }
    
    return nullptr;
}

void Ninja::update()
{
    if(currentAnimation != nullptr)
    {
        currentAnimation->update();
    }
}

void Ninja::render()
{
    switch (state)
    {
        case STAND:
            currentAnimation = getAnimation("stand");
            break;
            
        default:
            break;
    }
    
    if(currentAnimation != nullptr)
    {
        currentAnimation->setAnimationFlag(lookingDirection);
        currentAnimation->animate();
    }
        
}


Ninja* Ninja::loadChar(std::string filename_)
{
    Ninja *newNinja = nullptr;
    Sprite *currentSprite = nullptr;
    Jutsu *currentJutsu = nullptr;
    
    std::fstream file;
    std::string tag;
    std::string content;
    std::map<std::string, std::string> info;
    std::vector<std::string> seals;
    std::vector<std::string> args;
    std::string args_s;
    int x, y, w, h, count = 0;
    int start, end, cycles, delay, direction;
    int nrow, ncol;
    char buffer[4096];
    char sol = '\0';
    
    ResourceManager *resources = controller->getResources();
    
    file.open(filename_.c_str(), std::ios::in);
    
    if(!file.good())
    {
        std::cerr << "Could not open file: " <<filename_ << std::endl;
        return nullptr;
    }
    
    /* Read the basic stats */
    while(file.good())
    {
        sol = file.get();
        
        if(sol == '#')
        {
            file.getline(buffer, 2048, '\n');
        }
        else if(sol == '>')
        {
            /* Save previous content if any */
            if(!tag.empty())
                info.insert(std::pair<std::string, std::string>(tag, content));
            
            file >> tag;
            file.getline(buffer, 4095, '\n');
            content.assign(buffer);
            
            /*Erase first tab*/
            content.erase(content.begin()+0);
        }
        else if(sol == '*')
        {
            content.append("\n");
            
            file.getline(buffer, 4095, '\n');
            content.append(buffer);
        }
    }
    /* Save last tag read */
    if(!tag.empty())
        info.insert(std::pair<std::string, std::string>(tag, content));
    
    file.close();
    tag.clear();
    content.clear();
    
    newNinja = new Ninja(info.find("name")->second, info.find("element")->second, info.find("family")->second, info.find("description")->second);
    
    /* Load the basic animations */
    file.open(info.find("animations")->second.c_str(), std::ios::in);
    
    while(file.good())
    {
        sol = file.get();
        args.clear();
        
        if(sol == '#')
        {
            file.getline(buffer, 2048, '\n');
        }
        else if(sol == '>')
        {
            file >> tag;
            
            if(!tag.compare("name"))
            {
                //Save previous animation sprite if any
                if(currentSprite != nullptr)
                    newNinja->addAnimation(currentSprite);
                
                file.getline(buffer, 4095, '\n');
                args_s = removeTrailing(buffer);
                
                //Start a new animation sprite
                currentSprite = new Sprite(args_s.c_str());
            }
            else if(!tag.compare("image"))
            {
                int id = 0;
                file.getline(buffer, 4095, '\n');
                args_s = removeTrailing(buffer);
                
                ALLEGRO_BITMAP *loaded = resources->loadImage(args_s, tag);
                currentSprite->setImage(loaded);
            }
            else if(!tag.compare("frame"))
            {
                file >> x;
                file >> y;
                file >> w;
                file >> h;
                currentSprite->addFrame(Frame(count,x,y,w,h));
                count++;
            }
            else if(!tag.compare("nframes"))
            {
                file.getline(buffer, 32, '\n');
            }
            else if(!tag.compare("param"))
            {
                file >> start;
                file >> end;
                file >> cycles;
                file >> delay;
                file >> direction;
                currentSprite->setParameters(delay, direction, start, end, cycles);
            }
            else if(!tag.compare("clip"))
            {
                file >> nrow;
                file >> ncol;
                file >> w;
                file >> h;
                currentSprite->clip(nrow, ncol, w, h);
            }
            else if(!tag.compare("clipn"))
            {
                file >> x;
                file >> y;
                file >> nrow;
                file >> ncol;
                file >> w;
                file >> h;
                currentSprite->clip(x, y, nrow, ncol, w, h);
            }
        }
    }
    file.close();

    /* Load the jutsus and its animations */
    file.open(info.find("jutsus")->second.c_str(), std::ios::in);
    
    while(file.good())
    {
        sol = file.get();
        
        if(sol == '#')
        {
            file.getline(buffer, 2048, '\n');
        }
        else if(sol == '>')
        {
            file >> tag;
            
            if(!tag.compare("name"))
            {
                //Save previous jutsu if any
                if(currentJutsu != nullptr)
                {
                    currentJutsu->addAnimation(currentSprite);
                    newNinja->addJutsu(currentJutsu);
                    count = 0;
                }
                
                file.getline(buffer, 4095, '\n');
                
                //Start a new jutsu
                currentJutsu = new Jutsu(buffer);
            }
            else if(!tag.compare("seals"))
            {
                file.getline(buffer, 4095, '\n');
                content.assign(buffer);
                seals = tools::split(content, ' ');
                
                //For each seal map to a real HandSeal Object
                for(int i = 0; i < seals.size(); i++)
                {
                    HandSeal *seal = controller->getSealWithName(seals[i]);
                    
                    if(seal != nullptr)
                        currentJutsu->addSeal(seal);
                }
                
            }
            else if(!tag.compare("image"))
            {
                file.getline(buffer, 4095, '\n');
                args_s = removeTrailing(buffer);
                currentSprite->setImage(resources->loadImage(args_s.c_str(), tag));
            }
            else if(!tag.compare("frame"))
            {
                file >> x;
                file >> y;
                file >> w;
                file >> h;
                currentSprite->addFrame(Frame(count,x,y,w,h));
                count++;
            }
            else if(!tag.compare("nframes"))
            {
                file.getline(buffer, 32, '\n');
                
                //Start a new animation sprite
                currentSprite = new Sprite(currentJutsu->getName());
            }
            else if(!tag.compare("param"))
            {
                file >> start;
                file >> end;
                file >> cycles;
                file >> delay;
                file >> direction;
                currentSprite->setParameters(delay, direction, start, end, cycles);
            }
            else if(!tag.compare("clip"))
            {
                file >> nrow;
                file >> ncol;
                file >> w;
                file >> h;
                currentSprite->clip(nrow, ncol, w, h);
            }
            else if(!tag.compare("clipn"))
            {
                file >> x;
                file >> y;
                file >> nrow;
                file >> ncol;
                file >> w;
                file >> h;
                currentSprite->clip(x, y, nrow, ncol, w, h);
            }
        }
    }
    //Save previous jutsu if any
    if(currentJutsu != nullptr)
    {
        currentJutsu->addAnimation(currentSprite);
        newNinja->addJutsu(currentJutsu);
        count = 0;
    }
    
    file.close();
    
    return newNinja;
}

